<template>
  <div class="chat-container">
    <!-- 头部导航 -->
    <van-nav-bar
      title="AI学伴"
      left-arrow
      fixed
      safe-area-inset-top
      @click-left="backDetails()"
      class="chat-header"
    >
    </van-nav-bar>

    <!-- 消息列表 -->
    <div class="message-list" ref="messageListRef">
      <div
        class="message-wrapper"
        v-for="(message, index) in messages"
        :key="index"
      >
        <!-- 时间戳 -->
        <!-- <div class="time-stamp" v-if="shouldShowTime(message, index)">
          {{ formatTime(message.timestamp) }}
        </div> -->

        <!-- 消息气泡 -->
        <div
          class="message-item"
          :class="{
            'message-right': message.type === 'user',
            'message-left': message.type === 'ai',
          }"
        >
          <!-- AI头像 -->
          <div class="avatar" v-if="message.type === 'ai'">
            <van-image
              width="40"
              height="40"
              round
              fit="cover"
              :src="aiAvatar"
              alt="AI助手"
            />
          </div>

          <!-- 消息内容 -->
          <div class="message-bubble" :class="message.type">
            <div class="message-content">
              <div
                v-if="message.type === 'ai' && message.typing"
                class="typing-indicator"
              >
                <span></span>
                <span></span>
                <span></span>
              </div>
              <!-- AI消息使用markdown编辑器 -->
              <div v-else-if="message.type === 'ai'" class="markdownContent">
                <mdEditor
                  :toolbars="[]"
                  :footers="[]"
                  ref="mavonEditor"
                  class="mdEditor"
                  v-model="message.text"
                />
              </div>
              <!-- 用户消息使用v-html -->
              <div
                v-else-if="message.type === 'user'"
                v-html="message.text"
              ></div>
              <div
                class="message-btns"
                v-if="message.type === 'ai' && !isSending"
              >
                <span
                  class="shuziren"
                  v-show="this.shuzirenPid"
                  @click="playShuzirenAudio(message.text)"
                  ><img src="@/assets/images/voice-icon.png" alt="数字人讲解" />
                  数字人讲解</span
                >
                <span class="copy" @click="copyMessage(message.text)"
                  ><img src="@/assets/images/copy-icon.png" alt="复制" />
                  复制</span
                >
              </div>
            </div>
          </div>

          <!-- 用户头像 -->
          <div class="avatar" v-if="message.type === 'user'">
            <van-image
              width="40"
              height="40"
              round
              fit="cover"
              :src="userAvatar"
              alt="用户"
            />
          </div>
        </div>
      </div>

      <!-- 占位符，用于滚动到底部 -->
      <div ref="scrollAnchor" style="height: 1px"></div>
    </div>

    <!-- 输入区域 -->
    <div class="input-area fixed-input">
      <!-- 输入框 -->
      <div class="input-container" v-show="!isRecording">
        <!-- 切换按钮 -->
        <div class="switch-btn" @click="switchInputMode">
          <img src="@/assets/images/change-icon.png" alt="切换输入模式" />
        </div>

        <!-- 输入模式容器 -->
        <div class="input-wrapper" :class="{ 'voice-mode': isVoiceMode }">
          <!-- 文字输入模式 -->
          <template v-if="!isVoiceMode">
            <van-field
              v-model="userInput"
              placeholder="输入消息..."
              type="textarea"
              rows="1"
              autosize
              @keyup.enter="sendMessage"
              class="message-input"
              :disabled="inputDisabled || endStatus === 1"
            />

            <van-button
              :disabled="isSending || inputDisabled || endStatus === 1"
              @click="sendMessage"
              type="primary"
              size="small"
              round
              class="send-btn"
              :loading="isSending"
            >
              <van-icon name="send" v-if="!isSending" />发送
            </van-button>
          </template>

          <!-- 语音输入模式 -->
          <template v-else>
            <!-- 语音按钮 -->
            <div
              class="voice-button"
              @touchstart.prevent="startRecording"
              @touchmove.prevent="handleTouchMove"
              @touchend.prevent="stopRecording"
              @mousedown.prevent="startRecording"
              @mousemove.prevent="handleTouchMove"
              @mouseup.prevent="stopRecording"
            >
              <!-- 语音图标 -->
              <div class="voice-icon">
                <img src="@/assets/images/yuyin-icon.png" alt="语音输入" />
              </div>

              <!-- 录音动画 -->
              <div class="recording-animation" v-if="isRecording">
                <div class="wave-bar"></div>
                <div class="wave-bar"></div>
                <div class="wave-bar"></div>
                <div class="wave-bar"></div>
                <div class="wave-bar"></div>
              </div>
              <span class="voice-text">{{ recordingText }}</span>
            </div>
          </template>
        </div>
      </div>
    </div>

    <!-- 录音状态提示层 -->
    <div
      class="recording-overlay"
      v-if="isRecording"
      :class="{ 'cancel-mode': isCancelZone }"
    >
      <!-- 录音时长显示 -->
      <div class="recording-time-display">{{ recordingDuration }}</div>

      <!-- 录音动画 -->
      <div class="recording-animation-container">
        <!-- 声波动画 -->
        <div class="sound-waves">
          <div class="wave-line" v-for="i in 6" :key="i"></div>
        </div>
      </div>

      <!-- 底部发送区域 -->
      <div class="send-area">
        <div class="send-text">
          <span v-show="!isCancelZone">松开发送&nbsp;上滑取消</span>
          <span v-show="isCancelZone">取消发送</span>
        </div>
        <div class="send-icon">
          <img src="@/assets/images/sound-icon.png" alt="" />
        </div>
      </div>
    </div>

    <!-- 数字人语音 -->
    <div class="shuziren-audio" style="display: none">
      <audio
        ref="shuzirenAudio"
        :src="zhuzirenAudio"
        @ended="onAudioEnded"
        @playing="onAudioStarted"
        @error="onAudioError"
      ></audio>
    </div>
  </div>
</template>

<script>
import aiAvatar from "@/assets/images/lu-icon.png";
import userAvatar from "@/assets/images/stu-icon.png";
import { showToast } from "vant";
export default {
  data() {
    return {
      courseId: "",
      uploadedFiles: [],
      userInput: "",
      messages: [],
      messageRecord: [],
      aiAvatar,
      userAvatar,
      selectedFileIndex: [],
      isSelected: false, // 用于跟踪当前选择状态
      quotedText: "", // 新增引用文本
      uploadLoading: false,
      eventSource: null,
      connectId: null,
      fileId: null,
      isSending: false,
      stopHover: false,
      clearHover: false,
      sessionId: "",
      sseUrl: "",
      videoDialogVisible: false,
      videoData: null,
      sseResource: "", // sse资源数据
      isCollectingJson: false, // 标记是否正在收集JSON数据
      jsonBuffer: "", // 临时存储JSON数据
      kbId: null, // 知识库ID，用于进入考核时传递
      shuzirenPic: "",
      shuzirenPid: "",
      zhuzirenAudio: "",
      inputDisabled: false, // 控制输入框禁用状态
      classId: null,
      chapterId: null,
      endStatus: null, // 结束状态
      isVoiceMode: false, // 是否为语音输入模式
      isRecording: false, // 是否正在录音
      recordingText: "按住说话", // 录音状态文字
      startTouchY: 0, // 触摸开始Y坐标
      currentTouchY: 0, // 当前触摸Y坐标
      isCancelZone: false, // 是否进入取消区域
      mediaRecorder: null, // MediaRecorder对象
      audioStream: null, // 音频流
      recordingStart: null, // 录音开始时间
      recordingDuration: "00:00", // 录音持续时间
      recordingTimer: null, // 录音计时器
      audioChunks: [], // 音频数据块
      audioBlob: null, // 音频Blob对象
      waitingForPermission: false, // 是否正在等待用户授权麦克风
      pendingTouchEvent: null, // 保存待处理的触摸事件
      hasAudioPermission: false, // 是否已获取音频权限
      isRequestingPermission: false, // 是否正在请求权限
      isAudioPlaying: false, // 控制是否正在播放音频
      isShuzirenLoading: false, // 控制数字人是否正在加载音频
    };
  },
  watch: {
    "$route.query.chapterId": {
      handler(newVal) {
        this.courseId = JSON.parse(localStorage.getItem("courseId"));
        this.chapterId = newVal;
        this.checkAndGetChatRecord();
        this.getShuziren();
      },
      immediate: true, // 立即执行
    },
  },
  created() {
    this.getSessionId();
    this.getChatRecord();
  },
  methods: {
    // 检查参数并获取聊天记录
    checkAndGetChatRecord() {
      if (this.chapterId) {
        // this.getChatRecord();
      }
    },

    // 获取聊天记录
    getChatRecord() {
      const loading = this.$loading({
        lock: true,
        text: "加载中...",
        spinner: "el-icon-loading",
        background: "rgba(0, 0, 0, 0.7)",
      });
      this.$axios
        .get(
          `/tes/course/auto/make/list-record?contextId=0&courseId=${this.courseId}&type=5`
        )
        .then((res) => {
          this.messageRecord = res.data.record || [];
          this.endStatus = res.data.endStatus;
          // this.$emit("endFn", this.endStatus);
          if (this.messageRecord.length > 0) {
            this.sessionId = res.data.sessionId;
            // 有聊天记录，处理并展示记录
            this.processChatHistory(this.messageRecord);
          } else {
            this.messages = [
              {
                type: "ai",
                text: "hello！华电er，您的学科学习帮手来啦！ 从课程重点梳理、到专业术语释义、我将全方位助力您的学科学习。不妨告诉我您正在面临的问题，我来帮您解决～",
                isWelcome: true,
              },
            ];
          }
        })
        .finally(() => {
          loading.close();
        });
    },

    // 获取数字人
    getShuziren() {
      this.$axios
        .get(
          `/tes/course/auto/make/get-chapter-digital-human?chapterId=${this.chapterId}`
        )
        .then((res) => {
          this.shuzirenPid = res.data?.pid;
        });
    },

    // 新增：调用对话接口
    callDialogueAPI(prompt) {
      // 获取用户信息
      const userInfo = JSON.parse(localStorage.getItem("userInfo") || "{}");
      const userId = userInfo.id;

      if (!userId) {
        this.$message.error("获取用户信息失败");
        return;
      }

      // 先添加AI消息占位符（仅在开始时）
      if (prompt === "开始") {
        const aiMessage = {
          type: "ai",
          text: "",
          isWelcome: true,
          typing: true,
          videoData: null,
          documents: [],
          sseResource: null,
        };
        this.messages.push(aiMessage);
        this.isSending = true;
      }

      // 建立SSE连接
      this.connectEventSource(() => {
        let url = `/tes/course/auto/make/send-h5?type=5&connectId=${this.connectId}&courseId=${this.courseId}&chapterId=${this.chapterId}&sessionId=${this.sessionId}`;
        let requestData = {
          content: prompt,
        };

        this.$axios
          .post(url, requestData)
          .then((res) => {
            console.log("对话接口调用成功:", res);
          })
          .catch((error) => {
            console.warn("对话接口调用失败:", error);
            this.isSending = false;
          });
      });
    },

    // 处理聊天历史记录
    processChatHistory(records) {
      // 清空当前messages，保留欢迎语之外的内容
      this.messages = [];

      records.forEach((record) => {
        // 处理用户消息
        if (record.role == "user") {
          const userMessage = {
            type: "user",
            text: record.content?.replace(/\n/g, "<br>") || "",
          };
          this.messages.push(userMessage);
        }

        // 处理AI回复消息
        if (record.role == "assistant") {
          const aiMessage = {
            type: "ai",
            text: record.content?.replace(/\\n/g, "<br>") || "",
            files: record.files || [],
            isWelcome: false,
            videoData: record.video || null, // 数字人视频数据
            documents: record.resources || [], // 文档数据
            sseResource: record.sseResource || null, // SSE资源数据
            nextButton: record.nextButton || null, // 下一题按钮
            stopButton: record.stopButton || null, // 停止按钮
            courseTestButton: record.courseTestButton || null, // 进入考核按钮
            courseTestButtonName: record.courseTestButtonName || null, // 进入考核按钮名称
            courseTestButtonId: record.courseTestButtonId || null, // 进入考核按钮id
          };
          this.messages.push(aiMessage);
        }
      });

      // 滚动到最新消息
      this.scrollToBottom();
    },

    getSessionId() {
      const characters =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
      let result = "";
      for (let i = 0; i < 16; i++) {
        result += characters.charAt(
          Math.floor(Math.random() * characters.length)
        );
      }
      this.sessionId = result;
    },

    // 发送消息
    sendMessage() {
      if (this.userInput.trim() === "") {
        this.$message.error("请输入您想问的内容");
        this.userInput = "";
        return;
      }

      if (this.isSending) {
        this.$message.error("请等待上一次对话结束");
        return;
      }

      if (this.inputDisabled || this.endStatus === 1) {
        this.$message.error("当前考核已结束");
        return;
      }

      // 添加user消息
      const newMessage = {
        type: "user",
        quotedText: this.quotedText ? this.quotedText : "",
        text: this.userInput.replace(/\n/g, "<br>"),
        files: this.selectedFileIndex.map((index) => this.uploadedFiles[index]),
      };
      this.messages.push(newMessage);

      // 滚动到底部显示用户消息
      this.scrollToBottom();

      // 添加AI消息占位
      const aiMessage = {
        type: "ai",
        text: "",
        files: [],
        isWelcome: false,
        typing: true,
      };
      this.messages.push(aiMessage);

      // 调用对话接口
      this.callDialogueAPI(this.userInput);

      // 清空输入框和选中的文件
      const currentInput = this.userInput;
      this.userInput = "";
      this.quotedText = "";
      this.isSending = true;

      // 滚动到最新消息
      this.scrollToBottom();
    },

    // 建立sse连接
    connectEventSource(callback) {
      const that = this;

      if (window.EventSource) {
        // 如果已经存在连接，先关闭
        if (this.eventSource) {
          this.eventSource.close();
        }

        // 重置JSON收集状态
        this.isCollectingJson = false;
        this.jsonBuffer = "";
        this.sseResource = "";

        this.eventSource = new EventSource(`/tes/course/auto/make/sse/connect`);

        this.eventSource.onmessage = function (event) {
          const connectId = event.lastEventId;
          const data = event.data;

          if (connectId === data) {
            // 获取到连接id后执行回调
            that.connectId = connectId;
            if (callback) {
              callback();
            }
          } else {
            // 处理返回的消息流
            let currentMessage = that.messages[that.messages.length - 1];

            // 检查是否开始收集JSON数据
            if (data === "↖sseDone↘") {
              that.isCollectingJson = true;
              that.jsonBuffer = "";
              return;
            }

            // 检查是否结束收集JSON数据
            if (data === "↖Done↘") {
              if (that.isCollectingJson) {
                // 处理收集到的JSON数据
                try {
                  const parsedJson = JSON.parse(that.jsonBuffer);
                  that.sseResource = parsedJson;
                  console.log("提取到的JSON资源数据:", parsedJson);

                  // 检查 end 字段，如果为 1 则禁用输入框
                  if (parsedJson.end == 1) {
                    that.inputDisabled = true;
                  }

                  // 将sseResource数据存储到当前AI消息中，同时处理视频和文档资源
                  let currentMessage = that.messages[that.messages.length - 1];
                  if (currentMessage && currentMessage.type === "ai") {
                    currentMessage.sseResource = parsedJson;
                    // 处理视频数据
                    if (parsedJson.video && parsedJson.video.length > 0) {
                      currentMessage.videoData = parsedJson.video;
                    }
                    // 处理文档资源
                    if (
                      parsedJson.resources &&
                      parsedJson.resources.length > 0
                    ) {
                      currentMessage.documents = parsedJson.resources;
                    }
                  }
                } catch (error) {
                  console.warn(
                    "JSON解析失败:",
                    error,
                    "原始数据:",
                    that.jsonBuffer
                  );
                  // 如果解析失败，保存原始字符串
                  that.sseResource = that.jsonBuffer;

                  // 同样存储到当前AI消息中
                  let currentMessage = that.messages[that.messages.length - 1];
                  if (currentMessage && currentMessage.type === "ai") {
                    currentMessage.sseResource = that.jsonBuffer;
                  }
                }

                // 重置收集状态
                that.isCollectingJson = false;
                that.jsonBuffer = "";
              }

              // SSE流结束
              that.isSending = false;
              that.eventSource.close();
              that.eventSource = null;
              return;
            }

            // 如果正在收集JSON数据，将data添加到缓冲区
            if (that.isCollectingJson) {
              that.jsonBuffer += data;
              return;
            }

            // 确保当前消息是AI消息，且不在收集JSON状态
            if (currentMessage && currentMessage.type === "ai") {
              // 取消typing状态
              currentMessage.typing = false;
              // 处理特殊字符
              let processedData = data
                .replaceAll("↖emsp↘", " ")
                .replaceAll("↖Start↘", "")
                .replaceAll("↖br↘", "\n")
                .replaceAll("↖tab↘", "   ")
                .replaceAll("↖sseDone↘", "")
                .replaceAll("↖Done↘", "")
                // 处理每个字符都被双引号包围的情况（如："欢""迎" -> 欢迎）
                .replace(/"(.?)"/g, "$1")
                // 处理被分割的Unicode字符（如："\uD83D""\uDC49" -> \uD83D\uDC49）
                .replace(/"(\\u[0-9A-Fa-f]{4})""(\\u[0-9A-Fa-f]{4})"/g, "$1$2")
                // 处理单独的Unicode字符
                .replace(/"(\\u[0-9A-Fa-f]{4})"/g, "$1")
                // 将Unicode转义序列转换为实际字符（处理代理对）
                .replace(
                  /\\u([0-9A-Fa-f]{4})\\u([0-9A-Fa-f]{4})/g,
                  (match, high, low) => {
                    const highCode = parseInt(high, 16);
                    const lowCode = parseInt(low, 16);
                    // 检查是否为代理对
                    if (
                      highCode >= 0xd800 &&
                      highCode <= 0xdbff &&
                      lowCode >= 0xdc00 &&
                      lowCode <= 0xdfff
                    ) {
                      return String.fromCharCode(highCode, lowCode);
                    }
                    // 否则单独处理每个字符
                    return (
                      String.fromCharCode(highCode) +
                      String.fromCharCode(lowCode)
                    );
                  }
                )
                // 处理单独的Unicode字符
                .replace(/\\u([0-9A-Fa-f]{4})/g, (match, code) => {
                  return String.fromCharCode(parseInt(code, 16));
                })
                // 处理字面的换行符字符串
                .replaceAll('\\"n\\"', "\n")
                .replaceAll('"\\n"', "\n")
                .replaceAll("\\n", "\n")
                // 处理其他转义字符
                .replaceAll("\\t", "\t")
                .replaceAll('\\"', '"');

              // 更新markdown内容
              currentMessage.text += processedData;

              // 滚动到底部
              that.scrollToBottom();
            }
          }
        };

        this.eventSource.onerror = function (error) {
          that.eventSource.close();
          that.eventSource = null;
          that.isSending = false;
        };
      } else {
        this.$message.error("您的浏览器不支持 SSE，请更换浏览器");
        that.isSending = false;
      }
    },

    // 播放数字人语音
    playShuzirenAudio(messageText) {
      // 获取最新的AI消息
      // const lastAiMessage = this.messages
      //   .slice()
      //   .reverse()
      //   .find((msg) => msg.sender === "assistant" && msg.text);

      if (!messageText) {
        showToast("暂无可播放的语音内容");
        return;
      }

      // if (!this.shuzirenPid) {
      //   showToast("暂未设置数字人无法播放");
      //   return;
      // }

      // 设置音频播放状态和loading状态
      this.isAudioPlaying = true;
      this.isShuzirenLoading = true;

      const obj = {
        content: messageText,
        pid: this.shuzirenPid,
      };
      const loading = this.$loading({
        lock: true,
        text: "加载中....",
        background: "rgba(0, 0, 0, 0.7)",
      });
      this.$axios
        .post(`/tes/digital-human/tts`, obj)
        .then((res) => {
          this.zhuzirenAudio = res.data;
          this.isShuzirenLoading = false;
          loading.close();

          // 播放数字人语音（状态会在音频事件中自动设置）
          this.$nextTick(() => {
            if (this.$refs.shuzirenAudio && this.zhuzirenAudio) {
              this.$refs.shuzirenAudio.play().catch((error) => {
                console.warn("数字人语音播放失败:", error);
                // 播放失败时重置状态（也会触发onAudioError事件）
                this.isAudioPlaying = false;
                this.isShuzirenLoading = false;
              });
            }
          });
        })
        .catch((error) => {
          console.warn("获取数字人语音失败:", error);
          this.isShuzirenLoading = false;
          // 接口调用失败时，保持静态图片模式并重置播放状态
          this.isAudioPlaying = false;
          loading.close();
        });
    },
    // 播放数字人语音
    playShuzirenAudio(messageText) {
      // 获取最新的AI消息
      // const lastAiMessage = this.messages
      //   .slice()
      //   .reverse()
      //   .find((msg) => msg.sender === "assistant" && msg.text);

      if (!messageText || !this.shuzirenPid) {
        this.$message.warning("暂无可播放的语音内容");
        return;
      }

      // 设置音频播放状态和loading状态
      this.isAudioPlaying = true;
      this.isShuzirenLoading = true;

      const obj = {
        content: messageText,
        pid: this.shuzirenPid,
      };
      const loading = this.$loading({
        lock: true,
        text: "加载中....",
        background: "rgba(0, 0, 0, 0.7)",
      });
      this.$axios
        .post(`/tes/digital-human/tts`, obj)
        .then((res) => {
          this.zhuzirenAudio = res.data;
          this.isShuzirenLoading = false;
          loading.close();

          // 播放数字人语音（状态会在音频事件中自动设置）
          this.$nextTick(() => {
            if (this.$refs.shuzirenAudio && this.zhuzirenAudio) {
              this.$refs.shuzirenAudio.play().catch((error) => {
                console.warn("数字人语音播放失败:", error);
                // 播放失败时重置状态（也会触发onAudioError事件）
                this.isAudioPlaying = false;
                this.isShuzirenLoading = false;
              });
            }
          });
        })
        .catch((error) => {
          console.warn("获取数字人语音失败:", error);
          this.isShuzirenLoading = false;
          // 接口调用失败时，保持静态图片模式并重置播放状态
          this.isAudioPlaying = false;
          loading.close();
        });
    },

    // 数字人语音播放结束
    onAudioEnded() {
      console.log("数字人语音播放结束");
      // 语音播放结束后切换回静态图片模式并重置播放状态
      this.isAudioPlaying = false;
      this.isShuzirenLoading = false;
    },

    // 数字人语音开始播放
    onAudioStarted() {
      console.log("数字人语音开始播放");
      // 确保播放状态正确设置
      this.isAudioPlaying = true;
    },

    // 数字人语音播放错误
    onAudioError() {
      console.warn("数字人语音播放错误");
      // 播放错误时重置状态
      this.isAudioPlaying = false;
      this.isShuzirenLoading = false;
    },
    // 复制消息
    copyMessage(text) {
      try {
        if (navigator.clipboard) {
          navigator.clipboard.writeText(text).then(() => {
            showToast("消息已复制到剪贴板");
          });
        } else {
          // 降级处理 - 使用传统方法
          const textarea = document.createElement("textarea");
          textarea.value = text;
          document.body.appendChild(textarea);
          textarea.select();
          document.execCommand("copy");
          document.body.removeChild(textarea);
          showToast("消息已复制到剪贴板");
        }
      } catch (err) {
        showToast("复制失败，请手动复制");
      }
    },

    // 滚动到底部
    scrollToBottom() {
      this.$nextTick(() => {
        setTimeout(() => {
          const messageList = this.$refs.messageListRef;
          if (messageList) {
            // 强制滚动到最底部，使用 scrollTo 方法更精确
            messageList.scrollTo({
              top: messageList.scrollHeight,
              // behavior: "smooth",
            });
            return;
          }
        }, 200);
      });
    },

    // 换行处理
    handleKeyCode(event) {
      if (event.keyCode === 13) {
        if (event.shiftKey) {
          return;
        } else {
          event.preventDefault();
          this.sendMessage();
        }
      }
    },

    // 返回
    backDetails() {
      this.$router.push({
        path: "/courseDetails",
        query: { chapterId: this.chapterId },
      });
    },

    // 切换输入模式（文字/语音）
    switchInputMode() {
      this.isVoiceMode = !this.isVoiceMode;
      this.isRecording = false; // 切换模式时重置录音状态
    },

    // ======= 语音录制并上传为 WAV =======
    async startRecording(event) {
      try {
        // 保存触摸事件信息，用于权限授权后的处理
        this.pendingTouchEvent = event;

        if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
          this.$message.error("您的浏览器不支持录音功能");
          return;
        }

        if (this.isSending) {
          showToast("请等待上一次对话结束");
          return;
        }

        // 如果已有权限且流还在，直接开始录音
        if (this.hasAudioPermission && this.audioStream) {
          // 确保流还在激活状态
          const tracks = this.audioStream.getTracks();
          if (tracks.every((track) => track.readyState === "live")) {
            this.initiateRecording(event);
            return;
          } else {
            // 流已关闭，需要重新获取，但不需要重新请求权限
            this.audioStream = null;
          }
        }

        // 判断是否是第一次请求权限（用于区分是否需要显示提示）
        const isFirstTimeRequest = !this.hasAudioPermission;

        // 标记正在等待权限和请求权限
        this.waitingForPermission = true;
        this.isRequestingPermission = true;

        // 如果是第一次请求（会弹系统权限弹窗），立即清除触摸状态
        // 这样可以避免安卓/鸿蒙设备上权限弹窗打断触摸事件链导致的问题
        if (isFirstTimeRequest) {
          this.pendingTouchEvent = null;
        }

        // 请求麦克风权限（如果是第一次会弹出系统权限弹窗，否则直接返回流）
        this.audioStream = await navigator.mediaDevices.getUserMedia({
          audio: {
            sampleRate: 16000,
            channelCount: 1,
            echoCancellation: true,
            noiseSuppression: true,
          },
        });

        // 权限已获取
        this.hasAudioPermission = true;
        this.waitingForPermission = false;
        this.isRequestingPermission = false;

        if (isFirstTimeRequest) {
          // 第一次请求权限：提示用户重新按住
          console.log("麦克风权限已授予，请重新按住说话");
          showToast("麦克风权限已开启，请重新按住说话");
        } else {
          // 已有权限，只是重新获取流：检查是否还在触摸状态，如果是则直接开始录音
          if (this.pendingTouchEvent) {
            console.log("已有权限，重新获取流后直接开始录音");
            this.initiateRecording(event);
            this.pendingTouchEvent = null;
          } else {
            console.log("用户已松手");
          }
        }
      } catch (error) {
        console.error("启动录音失败:", error);
        this.waitingForPermission = false;
        this.isRequestingPermission = false;
        this.pendingTouchEvent = null;
        this.hasAudioPermission = false;

        if (error.name === "NotAllowedError") {
          this.$message.error("麦克风权限被拒绝，请允许访问麦克风");
        } else if (error.name === "NotFoundError") {
          this.$message.error("未找到麦克风设备");
        } else {
          this.$message.error("启动录音失败，请重试");
        }
        this.isRecording = false;
      }
    },

    // 初始化录音逻辑（从请求权限后开始实际录制）
    initiateRecording(event) {
      this.recordingDuration = "00:00";

      // 自动选择浏览器支持的音频格式
      let mimeType = "";
      if (MediaRecorder.isTypeSupported("audio/ogg;codecs=opus")) {
        mimeType = "audio/ogg;codecs=opus";
      } else if (MediaRecorder.isTypeSupported("audio/webm;codecs=opus")) {
        mimeType = "audio/webm;codecs=opus";
      } else {
        mimeType = "";
      }

      this.mediaRecorder = new MediaRecorder(this.audioStream, { mimeType });
      this.audioChunks = [];
      this.audioBlob = null;

      // 监听数据事件
      this.mediaRecorder.ondataavailable = (event) => {
        if (event.data.size > 0) {
          this.audioChunks.push(event.data);
        }
      };

      // 监听停止事件
      this.mediaRecorder.onstop = () => {
        console.log("录音结束，准备上传识别");
        this.processAudioData();
      };

      this.mediaRecorder.start();
      this.isRecording = true;
      this.recordingText = "松开发送";
      this.isCancelZone = false;

      // 记录触摸开始位置
      this.startTouchY = event.touches
        ? event.touches[0].clientY
        : event.clientY;
      this.currentTouchY = this.startTouchY;
      console.log("开始录音");
      this.recordingStart = Date.now();
      this.startRecordingTimer();

      console.log("开始录音，使用格式：", mimeType);
    },

    // 停止录音
    stopRecording() {
      // 如果正在请求权限或等待权限，清除等待状态
      if (this.isRequestingPermission || this.waitingForPermission) {
        console.log("用户在等待权限期间松手，取消录音");
        this.waitingForPermission = false;
        this.isRequestingPermission = false;
        this.pendingTouchEvent = null;
        // 如果已经获取了流，也要停止
        if (this.audioStream) {
          this.audioStream.getTracks().forEach((t) => t.stop());
          this.audioStream = null;
        }
        return;
      }

      if (this.mediaRecorder && this.mediaRecorder.state === "recording") {
        // 保存取消状态，避免在stopRecordingTimer中被重置
        const wasCancelled = this.isCancelZone;

        this.mediaRecorder.stop();
        this.isRecording = false;
        this.stopRecordingTimer();

        // 恢复取消状态给processAudioData使用
        if (wasCancelled) {
          this.isCancelZone = true;
        }

        if (this.audioStream) {
          this.audioStream.getTracks().forEach((t) => t.stop());
        }
        console.log("停止录音，取消状态:", wasCancelled);
      }
    },

    // 计时器
    startRecordingTimer() {
      this.recordingTimer = setInterval(() => {
        const seconds = Math.floor((Date.now() - this.recordingStart) / 1000);
        const minutes = Math.floor(seconds / 60);
        const remainingSeconds = seconds % 60;
        this.recordingDuration = `${String(minutes).padStart(2, "0")}:${String(
          remainingSeconds
        ).padStart(2, "0")}`;
      }, 1000);
    },
    stopRecordingTimer() {
      if (this.recordingTimer) {
        clearInterval(this.recordingTimer);
        this.recordingTimer = null;
        // 重置状态
        this.recordingText = "按住说话";
        this.isCancelZone = false;
        this.startTouchY = 0;
        this.currentTouchY = 0;
      }
    },

    // 处理录音数据：转成 WAV 再识别
    async processAudioData() {
      if (this.audioChunks.length === 0) {
        console.warn("没有录音数据");
        this.isCancelZone = false; // 重置状态
        return;
      }

      // 如果是取消录音，不进行语音识别
      if (this.isCancelZone) {
        console.log("录音已取消，不进行语音识别");
        showToast("录音已取消");
        this.isCancelZone = false; // 重置状态
        return;
      }

      // 将音频块合并为Blob
      const audioBlob = new Blob(this.audioChunks, { type: "audio/webm" });

      // 转成ArrayBuffer后再解码为PCM
      const arrayBuffer = await audioBlob.arrayBuffer();
      const audioCtx = new (window.AudioContext || window.webkitAudioContext)({
        sampleRate: 16000, // 强制采样率
      });
      const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);

      // 只取单声道
      const channelData = audioBuffer.getChannelData(0);
      const wavBuffer = this.encodeWAV(channelData, audioBuffer.sampleRate);

      // 转成Blob对象
      const wavBlob = new Blob([wavBuffer], { type: "audio/wav" });

      // 上传WAV音频给后端
      this.audioToText(wavBlob);

      // 处理完成后重置状态
      this.isCancelZone = false;
    },

    // PCM -> WAV编码函数
    encodeWAV(samples, sampleRate) {
      const buffer = new ArrayBuffer(44 + samples.length * 2);
      const view = new DataView(buffer);

      const writeString = (view, offset, string) => {
        for (let i = 0; i < string.length; i++) {
          view.setUint8(offset + i, string.charCodeAt(i));
        }
      };

      let offset = 0;

      // 写入WAV头
      writeString(view, offset, "RIFF");
      offset += 4;
      view.setUint32(offset, 36 + samples.length * 2, true);
      offset += 4;
      writeString(view, offset, "WAVE");
      offset += 4;
      writeString(view, offset, "fmt ");
      offset += 4;
      view.setUint32(offset, 16, true);
      offset += 4;
      view.setUint16(offset, 1, true);
      offset += 2; // PCM格式
      view.setUint16(offset, 1, true);
      offset += 2; // 单声道
      view.setUint32(offset, sampleRate, true);
      offset += 4;
      view.setUint32(offset, sampleRate * 2, true);
      offset += 4;
      view.setUint16(offset, 2, true);
      offset += 2;
      view.setUint16(offset, 16, true);
      offset += 2;
      writeString(view, offset, "data");
      offset += 4;
      view.setUint32(offset, samples.length * 2, true);
      offset += 4;

      // 写入PCM数据
      let idx = 44;
      for (let i = 0; i < samples.length; i++, idx += 2) {
        let s = Math.max(-1, Math.min(1, samples[i]));
        view.setInt16(idx, s < 0 ? s * 0x8000 : s * 0x7fff, true);
      }

      return buffer;
    },

    writeString(view, offset, string) {
      for (let i = 0; i < string.length; i++) {
        view.setUint8(offset + i, string.charCodeAt(i));
      }
    },

    // ======= 上传音频并识别（发送 WAV 文件） =======
    async audioToText(audioBlob) {
      try {
        const formData = new FormData();
        formData.append("file", audioBlob, "recording.wav");

        const res = await this.$axios.post(
          `/tes/graph/speech-recognition?format=wav`,
          formData
        );

        if (res.data) {
          this.userInput = res.data.trim();
          if (this.userInput) this.sendMessage();
        } else {
          this.$message.warning("未识别到语音内容，请重试");
        }
      } catch (error) {
        console.error("语音识别失败:", error);
        this.$message.error("语音识别失败，请重试");
      }
    },

    // 处理触摸移动（检测上滑取消）
    handleTouchMove(event) {
      if (!this.isRecording) return;

      this.currentTouchY = event.touches
        ? event.touches[0].clientY
        : event.clientY;
      const deltaY = this.startTouchY - this.currentTouchY;

      // 上滑超过60px进入取消区域
      if (deltaY > 60) {
        if (!this.isCancelZone) {
          this.isCancelZone = true;
          this.recordingText = "松开取消";
        }
      } else {
        if (this.isCancelZone) {
          this.isCancelZone = false;
          this.recordingText = "松开发送";
        }
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.chat-container {
  height: 100vh;
  height: 100dvh; /* 动态视口高度，更好地适配移动端 */
  background: #f5f6fb;
  position: relative;
  overflow: hidden; /* 避免出现滚动条 */
}

.chat-header {
  background: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  --van-nav-bar-title-text-color: #1f2937;
  --van-nav-bar-icon-color: #6b7280;
}

.fixed-header {
  position: fixed;
  top: 0;
  top: env(safe-area-inset-top, 0); /* 适配刘海屏 */
  left: 0;
  right: 0;
  z-index: 1000;
}

.message-list {
  position: absolute;
  top: calc(46px + env(safe-area-inset-top, 0)); /* 导航栏高度 + 安全区域 */
  left: 0;
  right: 0;
  bottom: 0;
  overflow-y: auto;
  padding: 16px 16px 80px; /* 上下边距，为固定输入框留空间 */

  &::-webkit-scrollbar {
    display: none;
  }
}

.message-wrapper {
  margin-bottom: 20px;
}

.time-stamp {
  text-align: center;
  color: #9ca3af;
  font-size: 24px;
  margin-bottom: 16px;
  position: relative;

  &::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 1px;
    background: #e5e7eb;
    z-index: 1;
  }

  &::after {
    content: attr(data-time);
    background: #f7f8fc;
    padding: 0 16px;
    position: relative;
    z-index: 2;
  }
}

.message-item {
  display: flex;
  margin-bottom: 16px;
  animation: fadeInUp 0.3s ease;

  &.message-right {
    justify-content: flex-end;

    .message-bubble {
      margin-right: 12px;
    }
  }

  &.message-left {
    justify-content: flex-start;

    .message-bubble {
      margin-left: 12px;
    }
  }
}

.avatar {
  flex-shrink: 0;

  :deep(.van-image) {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    border: 2px solid #fff;
  }
}

.message-bubble {
  max-width: 70%;
  position: relative;

  &.user {
    .message-content {
      background: #dce9ff;
      color: #1f2937;
      border-radius: 10px;
    }
  }

  &.ai {
    .message-content {
      background: #fff;
      color: #1f2937;
      border-radius: 10px;

      .markdownContent {
        .mdEditor {
          border: none;
          height: auto;
          // 隐藏左侧编辑区
          :deep(.md-editor-input-wrapper) {
            display: none;
          }
          // 隐藏中间分割线
          :deep(.md-editor-resize-operate) {
            width: 0;
          }
          :deep(.md-editor-content-wrapper) {
            width: 100% !important;
          }
          :deep(.md-editor-content) {
            display: block;
          }
          :deep(.md-editor-preview) {
            padding: 0;
            p {
              margin: 0;
            }
          }
        }
      }

      .message-btns {
        margin-top: 10px;
        display: flex;
        color: #457aff;
        font-size: 12px;
        cursor: pointer;
        .shuziren {
          margin-right: 20px;
        }
      }
    }
  }
}

.message-content {
  padding: 12px 16px;
  font-size: 16px;
  line-height: 1.5;
  word-break: break-word;
  position: relative;

  :deep(br) {
    line-height: 1.8;
  }
}

.typing-indicator {
  display: flex;
  align-items: center;
  gap: 4px;

  span {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #d1d5db;
    animation: typing 1.4s infinite ease-in-out;

    &:nth-child(2) {
      animation-delay: 0.2s;
    }

    &:nth-child(3) {
      animation-delay: 0.4s;
    }
  }
}

.input-area {
  background: #fff;
  border-top: 1px solid #f3f4f6;
  padding: 12px;
}

.fixed-input {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  background: #fff;
  border-top: 1px solid #f3f4f6;
  padding: 12px;
  padding-bottom: calc(
    12px + env(safe-area-inset-bottom, 0)
  ); /* 底部安全区域适配 */
}

.input-container {
  position: relative;
  display: flex;
  align-items: flex-end;
  align-items: center;
  gap: 12px;
}

.input-wrapper {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 12px;
  background: #f8fafc;
  border-radius: 24px;
  padding: 8px;
  border: 1px solid #e2e8f0;
  transition: all 0.2s ease;

  &:focus-within {
    border-color: #667eea;
    box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
  }

  // 语音模式下的样式
  &.voice-mode {
    background: transparent;
    border: none;
    padding: 0;
    border-radius: 0;

    &:focus-within {
      border-color: transparent;
      box-shadow: none;
    }
  }
}

.message-input {
  flex: 1;
  background-color: #f8fafc;
  :deep(.van-field__control) {
    background: transparent;
    border: none;
    resize: none;
    font-size: 16px;
    line-height: 1.5;
    max-height: 120px;

    &::placeholder {
      color: #9ca3af;
    }
  }

  :deep(.van-field__word-limit) {
    font-size: 14px;
    color: #9ca3af;
  }
}

.send-btn {
  background: #457aff;
  flex-shrink: 0;
  min-width: 64px;
  transition: all 0.2s ease;

  &:not(:disabled):active {
    transform: scale(0.95);
  }

  &:disabled {
    opacity: 0.5;
  }
}

// 切换按钮样式
.switch-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  cursor: pointer;
  transition: all 0.2s ease;
  flex-shrink: 0;

  &:hover {
    background: #f1f5f9;
    border-color: #cbd5e1;
  }

  &:active {
    transform: scale(0.95);
  }

  img {
    width: 20px;
    height: 20px;
  }
}

// 语音输入模式样式（现在集成在input-wrapper中）

// 语音图标样式（现在在voice-button内部）
.voice-icon {
  display: flex;
  align-items: center;
  justify-content: center;

  img {
    width: 16px;
    height: 20px;
  }
}

// 语音按钮样式
.voice-button {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 40px;
  background: linear-gradient(90deg, #1842f9, #3279fb);
  color: white;
  border-radius: 20px;
  cursor: pointer;
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;

  .voice-text {
    font-size: 14px;
    font-weight: 500;
    z-index: 2;
    position: relative;
  }
}

// 录音脉冲动画
@keyframes recordingPulse {
  0% {
    box-shadow: 0 0 0 0 rgba(255, 71, 87, 0.4);
  }
  70% {
    box-shadow: 0 0 0 10px rgba(255, 71, 87, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(255, 71, 87, 0);
  }
}

// 取消区域脉冲动画
@keyframes cancelZonePulse {
  0% {
    box-shadow: 0 0 0 0 rgba(255, 55, 66, 0.6);
  }
  50% {
    box-shadow: 0 0 0 8px rgba(255, 55, 66, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(255, 55, 66, 0);
  }
}

// 录音动画样式
.recording-animation {
  display: flex;
  align-items: center;
  gap: 3px;
  position: absolute;
  left: 16px;
  z-index: 1;

  .wave-bar {
    width: 3px;
    height: 8px;
    background: rgba(255, 255, 255, 0.8);
    border-radius: 2px;
    animation: waveAnimation 1.2s ease-in-out infinite;

    &:nth-child(1) {
      animation-delay: 0s;
    }

    &:nth-child(2) {
      animation-delay: 0.1s;
    }

    &:nth-child(3) {
      animation-delay: 0.2s;
    }

    &:nth-child(4) {
      animation-delay: 0.3s;
    }

    &:nth-child(5) {
      animation-delay: 0.4s;
    }
  }
}

// 录音覆盖层样式
.recording-overlay {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 100%;
  background: rgba(0, 0, 0, 0.7);
  z-index: 9999;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  animation: slideUpIn 0.3s ease;
}

// 录音时长显示
.recording-time-display {
  font-size: 24px;
  color: white;
  text-align: center;
  margin-bottom: 10px;
}

// 录音动画容器
.recording-animation-container {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.sound-waves {
  display: flex;
  align-items: center;
  gap: 3px;
  justify-content: center;
  margin-bottom: 20px;

  .wave-line {
    width: 3px;
    background: linear-gradient(90deg, #1842f9, #3279fb);
    border-radius: 2px;
    animation: soundWaveAnimation 1.5s ease-in-out infinite;

    &:nth-child(1) {
      height: 12px;
      animation-delay: 0s;
    }
    &:nth-child(2) {
      height: 20px;
      animation-delay: 0.1s;
    }
    &:nth-child(3) {
      height: 16px;
      animation-delay: 0.2s;
    }
    &:nth-child(4) {
      height: 24px;
      animation-delay: 0.3s;
    }
    &:nth-child(5) {
      height: 14px;
      animation-delay: 0.4s;
    }
    &:nth-child(6) {
      height: 20px;
      animation-delay: 0.5s;
    }
  }
}

// 底部发送区域
.send-area {
  width: 100%;
  height: 120px;
  background: white;
  border-radius: 30px 30px 0 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  transition: all 0.3s ease;

  .send-text {
    text-align: center;

    span {
      color: #333;
      font-size: 14px;
    }
  }
}

// 录音相关动画
@keyframes recordingCirclePulse {
  0% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(69, 122, 255, 0.7);
  }
  70% {
    transform: scale(1.05);
    box-shadow: 0 0 0 15px rgba(69, 122, 255, 0);
  }
  100% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(69, 122, 255, 0);
  }
}

@keyframes soundWaveAnimation {
  0%,
  100% {
    transform: scaleY(0.5);
    opacity: 0.7;
  }
  50% {
    transform: scaleY(1.2);
    opacity: 1;
  }
}

@keyframes slideUpIn {
  from {
    opacity: 0;
    transform: translateY(100%);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: scale(0.9);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

// 动画
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes typing {
  0%,
  60%,
  100% {
    transform: scale(0.8);
    opacity: 0.5;
  }
  30% {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes waveAnimation {
  0% {
    height: 8px;
    transform: scaleY(1);
  }
  50% {
    height: 20px;
    transform: scaleY(1.8);
  }
  100% {
    height: 8px;
    transform: scaleY(1);
  }
}

// // 响应式适配
// @media (max-width: 375px) {
//   .message-bubble {
//     max-width: 75%;
//   }

//   .message-content {
//     font-size: 16px;
//     padding: 10px 14px;
//   }

//   .input-area {
//     padding: 12px;
//   }
// }

// // 深色模式适配（可选）
// @media (prefers-color-scheme: dark) {
//   .chat-container {
//     background: linear-gradient(180deg, #1f2937 0%, #111827 100%);
//   }

//   .chat-header {
//     background: #1f2937;
//     --van-nav-bar-title-text-color: #f9fafb;
//     --van-nav-bar-icon-color: #d1d5db;
//   }

//   .message-bubble.ai .message-content {
//     background: #374151;
//     color: #f9fafb;
//     border-color: #4b5563;
//   }

//   .input-wrapper {
//     background: #374151;
//     border-color: #4b5563;
//   }

//   .input-area {
//     background: #1f2937;
//     border-top-color: #4b5563;
//   }
// }
</style>
